#ifndef BS2_BENCHMARK_HPP
#define BS2_BENCHMARK_HPP

#include <algorithm>
#include <vector>

#include <boost/signals2.hpp>
#include <boost/bind.hpp>

#include "benchmark.hpp"

namespace benchmark_class
{

class Bs2 : public boost::signals2::trackable
{
    std::size_t m_data[c_payload];

    NOINLINE(void method_one(Rng_t& rng) const)
    {
        volatile std::size_t a = rng();
        (void)a;
    }
    NOINLINE(void method_two(std::size_t a, Rng_t& rng))
    {
        volatile std::size_t b = rng();
        (void)a;
        (void)b;
    }

    public:

//------------------------------------------------------------------------------

    NOINLINE(static double runtime(Rng_t& rng, Eng_t& eng, std::size_t N,
        std::size_t emission_ratio, std::size_t timer_limit))
    {
        typedef benchmark_class::Bs2 Foo;

        std::size_t count = 0;

        std::vector<std::size_t> random_index(N);
        std::generate(random_index.begin(), random_index.end(), IncrementFill());
        std::shuffle(random_index.begin(), random_index.end(), rng);

        g_timer.reset();
        do
        {
            // construction overhead
            std::vector<Foo> obj(N);

            boost::signals2::signal<void(Rng_t&)> signal_one;
            boost::signals2::signal<void(std::size_t, Rng_t&)> signal_two;

            for (std::size_t index : random_index)
            {
                // time operation overhead
                if (eng(rng) > emission_ratio)
                {
                    Foo* ptr = &obj[index];
                    signal_one.connect(boost::bind(&Foo::method_one, ptr, _1));
                    signal_two.connect(boost::bind(&Foo::method_two, ptr, _1, _2));
                }
                else
                {
                    signal_one(rng);
                    signal_two(index, rng);
                }
            }
            ++count;
            // destruction overhead
        }
        while (timer_limit > g_timer.count<Timer_u>());

        return std::chrono::duration_cast<Delta_u>
            (Timer_u(timer_limit / count)).count();
    }

//------------------------------------------------------------------------------
    NOINLINE(static std::size_t memory(std::size_t start_n,
        std::size_t maximum_n, std::size_t emission_ratio))
    {
        typedef benchmark_class::Bs2 Foo;

        Rng_t rng; // synchronized initial rng state
        Eng_t eng (0, 100);

        for (std::size_t N = start_n; N <= maximum_n; N *= 2)
        {
            std::vector<std::size_t> random_index(N);
            std::generate(random_index.begin(), random_index.end(), IncrementFill());
            std::shuffle(random_index.begin(), random_index.end(), rng);

            std::vector<Foo> obj(N);

            boost::signals2::signal<void(Rng_t&)> signal_one;
            boost::signals2::signal<void(std::size_t, Rng_t&)> signal_two;

            for (std::size_t index : random_index)
            {
                if (eng(rng) > emission_ratio)
                {
                    Foo* ptr = &obj[index];
                    signal_one.connect(boost::bind(&Foo::method_one, ptr, _1));
                    signal_two.connect(boost::bind(&Foo::method_two, ptr, _1, _2));
                }
                else
                {
                    signal_one(rng);
                    signal_two(index, rng);
                }
            }
        }
        return rng(); // return rng state
    }
};

} // namespace benchmark_class -------------------------------------------------

#endif // BS2_BENCHMARK_HPP
